﻿
using System;
using System.Text;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;

using alib.Debugging;
using alib.String;
using alib.Enumerable;
using alib.Collections;
using alib.Collections.ReadOnly;
using alib.Dictionary;
using alib.BitArray;
using alib.Memory;

namespace agree
{

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public unsafe abstract partial class _atfs_base : Tfs
	{
		protected _atfs_base(IRtParent parent, Tfs to_clone)
			: base(parent, to_clone)
		{
			var atfs = to_clone as _atfs_base;
			if (atfs != null)
				this.varbits = atfs.varbits;
		}
		protected _atfs_base(IRtParent parent, IIdentity prv, Restrictor r, Flags opts)
			: base(parent, prv, r, opts)
		{
		}


		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public const int SHL_PS = 5;
		public ulong varbits;

		public void Dealloc()
		{
			c_act = 0;
			entries = null;
		}

#if false
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public bool GetEdgeAtPath(Edge e_start, FsPath p, out ConstraintRef cref)
		{
			if (p.IsEmpty)
			{
				cref = default(ConstraintRef);
				return false;
			}
			Edge e = e_start;
			Edge e_prev;
			for (int i = 0; ; )
			{
				e_prev = e;
				int i_feat = p[i];
				//int i_feat = tm.GetFeatureIndex(p[i]);
				if (i_feat == -1 || !TryGetEdge(i_feat, e.Mark, out e))
				{
					cref = default(ConstraintRef);
					return false;
				}
				i++;
				if (i == p.Count)
				{
					cref = new ConstraintRef(this, e_prev, i_feat);
					return true;
				}
			}
		}
#endif

#if REVERSE_INDEX
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// NodeId interpretation
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public Type GetNodeType(int node_id)
		{
			return GetIx1Type(GetNodeIx1(node_id));
		}

		public int GetNodeId(int ix1)
		{
			int m;
			return ix1 == 0 || (m = entries[ix1 - 1].e_Mark) >= 0 ? ix1 : m;
		}
		public int GetNodeIx1(int node_id)
		{
			//if (this is TfsBuilder.Xdl)

			if (node_id == 0)
				throw new Exception();
			return node_id < 0 ? _reverse_index[node_id][0] : node_id;
		}
		public Edge.Flag GetNodeEdgeFlags(int node_id)
		{
			if (node_id == 0)
			{
				if (this is TfsBuilder.Xdl)
					throw new Exception();
				return _top_edge.FlagsId;
			}
			int[] ret;
			if (node_id < 0)
			{
				ret = _reverse_index[node_id];
				Debug.Assert(ret[0] > 0);
				return entries[ret[0] - 1].e_FlagsId;
			}
			return entries[node_id - 1].e_FlagsId;
		}
		public int GetNodeMark(int node_id)
		{
			if (node_id == 0)
			{
				if (this is TfsBuilder.Xdl)
					throw new Exception();
				return _top_edge.Mark;
			}
			int[] ret;
			if (node_id < 0)
			{
				ret = _reverse_index[node_id];
				Debug.Assert(ret[0] > 0);
				return entries[ret[0] - 1].e_Mark;
			}
			return entries[node_id - 1].e_Mark;
		}
		/// <summary>includes topmost edge</summary>
		public int NodeCount()
		{
			return 1 + Enumerable.Range(1, (int)c_act).CountDistinct(ix1 => GetNodeId(ix1));
		}
		public HashSet<int> Nodes()
		{
			HashSet<int> hs = new HashSet<int>();
			hs.Add(0);
			for (int i = 1; i <= c_act; i++)
				hs.Add(GetNodeId(i));
			return hs;
		}
		public IEnumerable<int> GetNodeParents(int node_id)
		{
			if (node_id == 0)
				return Collection<int>.None;
			int m = node_id < 0 ? node_id : entries[node_id - 1].mark;
			int[] ret = _reverse_index[m];
			return ret.Select(ix1 => GetNodeId(ix1));
		}
		public IEnumerable<arc> GetNodeInArcs(int nid_lower)
		{
			if (nid_lower == 0)
				yield break;
			if (nid_lower > 0)
			{
				var arr = _reverse_index[entries[nid_lower - 1].mark];
				//Debug.Assert(arr.Length == 1);
				yield return new arc
				{
#if WITH_TFS
					tfs = this,
#endif
					nid_upper = GetNodeId(arr[0]),
					i_feat = entries[nid_lower - 1].i_feat,
					nid_lower = nid_lower,
				};
			}
			else
			{
				foreach (int ix1_upper in _reverse_index[nid_lower])
				{
					Debug.Assert(ix1_upper != 0);
					int m_upper = entries[ix1_upper - 1].mark;
					yield return new arc
					{
#if WITH_TFS
						tfs = this,
#endif
						nid_upper = GetNodeId(_reverse_index[m_upper][0]),
						i_feat = entries[ix1_upper - 1].i_feat,
						nid_lower = nid_lower,
					};
				}
			}
		}
		public int NodeOutArcCount(int nid_upper)
		{
			int m = GetNodeMark(nid_upper);
			if (m == 0)
				return 0;
			int c = 0;
			for (int i = 0; i < c_act; i++)
				if (entries[i].mark == m)
					c++;
			return c;
		}

		[DebuggerDisplay("{ToString(),nq}")]
		public struct arc
		{
#if WITH_TFS
			public _atfs_base tfs;
#endif
			public int nid_upper;
			public int i_feat;
			public int nid_lower;
#if WITH_TFS
			public override string ToString()
			{
				int ix1 = tfs.GetNodeIx1(nid_upper);
				TypeMgr tm = tfs.GetIx1TypeMgr(ix1);
				if (tm == null)
					return "(no TypeMgr)";
				return String.Format("{0,3} {1,14} → {2,10} → {3,3} {4,14}",
					nid_upper,
					tfs.GetNodeType(nid_upper).Name,
					fcm.feat_arr[i_feat].feature.ToUpper(),
					nid_lower,
					tfs.GetNodeType(nid_lower).Name);
			}
#endif
		};

		public int[] GetOutMarkSlots(int out_mark)
		{
			int[] ret;
			if (_reverse_index.TryGetValue(out_mark, out ret))
				return ret;
			return Collection<int>.None;
		}

		public Ix1Path[] Ix1Paths(int ix1)
		{
			if (ix1 == 0 || ix1 > c_act)
				return Collection<Ix1Path>.None;

			var L = _ix1_paths(ix1).ToList();

			Ix1Path[] arr = new Ix1Path[L.Count];
			for (int i = 0; i < arr.Length; i++)
				arr[i] = new Ix1Path(this, L[i]);

			//Array.Sort(arr, TfsPath.Comparer);
			return arr;
		}
#endif

		public IEnumerable<int> _up(int om)
		{
			if (om == 1)
				yield return 0;

			int c = this.SlotCount;
			for (int i = 0; i < c; i++)
			{
				if (entries[i].e_Mark == om)
					yield return i + 1;
			}
		}

		/// calculate path without buidling reverse dictionary
		public IEnumerable<int[]> _ixp2(int ix1)
		{
			if (ix1 == 0 || ix1 > this.SlotCount)
				yield return ZeroRange.Singleton;
			else
			{
				foreach (int q in _up(entries[ix1 - 1].mark))
					foreach (int[] up in _ixp2(q))
						yield return alib.Array.arr.Append(up, ix1);
			}
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// reverse index for debugging and diagnostics
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		WeakReference wr_reverse_index;

		///<summary> out-mark => slot index (1-based, zero for TopMost)</summary>
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public ReadOnlyDictionary<int, int[]> _reverse_index
		{
			get
			{
				var _wr = wr_reverse_index;
				ReadOnlyDictionary<int, int[]> _revix;
				if (_wr == null)
				{
					if (entries == null || c_act == 0)
						return ReadOnlyDictionary<int, int[]>.Empty;
					_wr = new WeakReference(null, false);
					_wr = Interlocked.CompareExchange(ref wr_reverse_index, _wr, null) ?? _wr;
				}
				if ((_revix = (ReadOnlyDictionary<int, int[]>)_wr.Target) == null)
					_wr.Target = _revix = _rebuild_reverse_index();
				return _revix;
			}
		}


		// out-mark => slot index (1-based, zero for TopMost)
		protected virtual ReadOnlyDictionary<int, int[]> _rebuild_reverse_index()
		{
			int[] non_coref = new int[c_act + 1];
			int ncm_max = int.MinValue;
			RefList<int>[] L_coref = new RefList<int>[c_corefs];

			for (int ix = 0; ix < c_act; )
			{
				int om = entries[ix].e_Mark;
				ix++;
				if (om > 0)
				{
					non_coref[om] = ix;
					if (om > ncm_max)
						ncm_max = om;
				}
				else if (om < 0)
					(L_coref[~om] ?? (L_coref[~om] = new RefList<int>())).Add(ix);
			}
			Debug.Assert(non_coref[_top_edge.Mark] == 0);

			return non_coref
					.Take(ncm_max + 1)
					.TagIndex()
					.Where(a => a.Item != 0 || a.Index == _top_edge.Mark)
					.Select(a => new KeyValuePair<int, int[]>(a.Index, new int[] { a.Item }))
					.Concat(L_coref.Select((L, notm) => new KeyValuePair<int, int[]>(~notm, L.GetTrimmed())))
					.ToReadOnlyDictionary();
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// Path stuff
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

		IEnumerable<int[]> _ix1_paths(int ix1)
		{
			if (ix1 == 0 || ix1 > c_act)
				yield return ZeroRange.Singleton;
			else
				foreach (int q in _reverse_index[entries[ix1 - 1].mark])
					foreach (int[] up in _ix1_paths(q))
						yield return alib.Array.arr.Append(up, ix1);
		}

		public Ix1Path[] OutMarkPaths(int om) { return OutMarkPaths(ftm, om); }
		public Ix1Path[] OutMarkPaths(FeatMgr ftm, int om)
		{
			if (om == 0)
				return Collection<Ix1Path>.None;

			var L = _reverse_index[om].SelectMany(q => _ix1_paths(q)).ToList();

			Ix1Path[] arr = new Ix1Path[L.Count];
			for (int i = 0; i < arr.Length; i++)
				arr[i] = new Ix1Path(this, L[i]);

			//Array.Sort(arr, TfsPath.Comparer);
			return arr;
		}


		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// CSV export
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

		public String CsvExport(TypeLattice _unused)
		{
			StringBuilder sb = new StringBuilder();
			if (!(this is MultiRootTfs))
				sb.AppendFormat("{0}\t{1}\t{2}\t{3}\r\n", "", "", tu.TypeNameOrStringValue(_top_edge.FlagsId), _top_edge.Mark);

			for (int i = 0; i < c_act; i++)
			{
				arr_tfs_entry ate = entries[i];
				//TypeMgr _tm = GetIx1TypeMgr(i + 1);
				//if (_tm == null)
				//	return "(no TypeMgr)";

				sb.AppendFormat("{0}\t{1}\t{2}\t{3}\r\n",
					ate.mark,
					ftm[ate.i_feat].feature.ToUpper(),
					tu.TypeNameOrStringValue(ate.e_FlagsId),
					ate.e_Mark);
			}
			return sb.ToString();
		}
	};
}
